package logger.sd.server.service;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import org.apache.log4j.Level;

import logger.sd.client.ClientLogger;
import logger.sd.protocol.LRP;
import logger.sd.server.LoggingServer;
import logger.sd.server.MonothreadLoggingServer;
import logger.sd.server.ServerConfiguration;

public class ReplicationTest implements Observer {

	private List<LoggingServer> serverList;
	private List<ServerConfiguration> configList;

	private Replicator replicator;
	private boolean sending;
	private static int count = 0;

	/**
	 * Construtor
	 */
	public ReplicationTest() {
		initializeTest();
		for (LoggingServer server : serverList) {
			server.addListener(this);
		}
		sending = false;
		replicator = new SimpleReplicator(serverList.get(0));
	}

	/**
	 * Inicializador
	 */
	private void initializeTest() {

		configList = new ArrayList<ServerConfiguration>();
		configList.add(new ServerConfiguration("config01"));
		configList.add(new ServerConfiguration("config02"));

		String[][] argsTemp = {
				{ "7070", "localhost:7071", "log00.log", "localhost:7071",
						"-first" },
				{ "7071", "localhost:7070", "log01.log", "localhost:7070" }, };

		serverList = new ArrayList<LoggingServer>();

		for (int i = 0; i < configList.size(); ++i) {
			parse(configList.get(i), argsTemp[i]);
			serverList.add(new MonothreadLoggingServer(configList.get(i)));
		}
	}

	/**
	 * Funcao que le os argumentos da linha de comando.
	 * 
	 * @param args
	 */
	private void parse(ServerConfiguration config, String[] args) {

		config.setFirst(false);
		List<String> servers = new ArrayList<String>();

		config.setPort(args[0]);
		String[] address = args[1].split(":");
		config.setNextHost(address[0]);
		config.setNextPort(address[1]);
		config.setLogfile(args[2]);
		String[] others = args[3].split(",");
		for (int i = 0; i < others.length; ++i) {
			servers.add(others[i]);
		}
		config.setOthersServers(servers);

		if (args.length > 4) {
			config.setFirst(args[4].equals("-first"));
		}
	}

	/**
	 * Inici aos servidores
	 */
	public void runServers() {

		Thread t01 = new Thread(new Runnable() {

			@Override
			public void run() {
				serverList.get(0).start();
			}
		});
		t01.start();

		Thread t02 = new Thread(new Runnable() {

			@Override
			public void run() {
				serverList.get(1).start();
			}
		});
		t02.start();

	}

	/**
	 * Testa a passagem de token
	 */
	public void initTestes() {
		System.out.println("========= Teste de Replicacao =======");
		this.runServers();
	}

	/**
	 * Testa a insercao no buffer
	 */
	public void testInsertBuffer() {
		System.out.println("== Teste insert buffer");

		String msg = "Msg de teste";
		String req = LRP.ECHO.getMessage() + ':' + msg;
		List<String> buff = new ArrayList<String>();
		replicator.insertBuffer(LRP.ECHO.getMessage(), "Msg de teste");
		buff.add(req);
		if (checkFile(buff, replicator.getFileBufferName())) {
			System.out.println("testInsertBuffer concuido com sucesso!");
		} else {
			System.err.println("testInsertBuffer concuido com erros!");
		}
		System.out.println("==============================");
	}

	/**
	 * Testa o envio da mensagem de replciacao
	 */
	public void testSend() {

		System.out.println("== Teste send");
		sending = true;

		Thread t = new Thread(new Runnable() {

			@Override
			public void run() {
				ClientLogger logger = ClientLogger.getInstance();

				logger.connect("localhost", serverList.get(1).getConfig()
						.getPort());
				System.out.println("Conectado em "
						+ serverList.get(1).getConfig().getPort());

				logger.setLevel(Level.ALL);

				logger.debug("Mensagem 01");			
				logger.error("Notificacao 02");
				logger.info("Mensagem 03");
				logger.warn("Mensagem 04");
				logger.fatal("Mensagem 05");

				logger.end();

				System.out.println("Fim da execucao do cliente");
			}
		});

		t.start();

	}

	/**
	 * Checa se a string estah no arquivo
	 * 
	 * @param s
	 *            String a ser pesquisada
	 * @param caminho
	 *            Caminnho do arquivo
	 * @return True, se s estah no arquivo, e false em caso contr�rio.
	 */
	private Boolean checkFile(List<String> s, String caminho) {

		try {
			File file = new File(caminho);
			FileReader reader = new FileReader(file);
			BufferedReader br = new BufferedReader(reader);
			List<String> aux = new ArrayList<String>();

			for (int i = 0; i < s.size(); ++i) {
				aux.add(br.readLine());
			}

			for (int i = 0; i < s.size(); ++i) {
				if (!s.get(i).equals(aux.get(i))) {
					System.err.println("String nao confere:");
					System.err.println(s.get(i) + "!=" + aux.get(i));
					return false;
				}
			}
			System.out.println("Mensagem checada com sucesso em " + caminho);

			br.close();
			reader.close();
			file.delete();
			return true;

		} catch (FileNotFoundException e) {
			System.err.println("Arquivo nao encontrado.");
			e.printStackTrace();
		} catch (IOException e) {
			System.err.println("Erro de IO.");
			e.printStackTrace();
		}
		return true;
	}

	/**
	 * Checa se a string estah no arquivo
	 * 
	 * @param s
	 *            String a ser pesquisada
	 * @param caminho
	 *            Caminnho do arquivo
	 * @return True, se s estah no arquivo, e false em caso contr�rio.
	 */
	private void printFile(String caminho) {

		System.out.println("==============================");
		System.out.println("= Conteudo de " + caminho + ":");
		try {
			File file = new File(caminho);
			FileReader reader = new FileReader(file);
			BufferedReader br = new BufferedReader(reader);
			List<String> aux = new ArrayList<String>();
			String temp = "";

			while ((temp = br.readLine()) != null) {
				aux.add(temp);
			}

			for (String s : aux) {
				System.out.println(s);
			}

			reader.close();
			file.delete();

		} catch (FileNotFoundException e) {
			System.err.println("Arquivo nao encontrado.");
			e.printStackTrace();
		} catch (IOException e) {
			System.err.println("Erro de IO.");
			e.printStackTrace();
		}
	}

	/**
	 * Implementa o Observer
	 */
	@Override
	public void update(Observable arg0, Object arg1) {
		Integer aqui = ((LoggingServer) arg0).getConfig().getPort();
		System.out.println("O token chegou em localhost:" + aqui);

		if (sending) {
			count++;
		}

		if (count >= 2) {
			try {
				Thread.sleep(3000L);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			printFile(serverList.get(0).getConfig().getLogFilename());
			printFile(serverList.get(1).getConfig().getLogFilename()
					+ " - replica");
			System.exit(0);
		}
	}

	/**
	 * Main
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		ReplicationTest test = new ReplicationTest();
		test.initTestes();
		test.testInsertBuffer();
		try {
			Thread.sleep(2000L);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		test.testSend();

	}
}
